package com.dmj.sqldsl.executor.visitor;

import static java.util.stream.Collectors.joining;

import com.dmj.sqldsl.executor.exception.ExecutionException;
import com.dmj.sqldsl.model.Entity;
import com.dmj.sqldsl.model.column.ValueColumn;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;

@Getter
public class TableEntityVisitor {

  private final List<Parameter> params;

  private String sql;

  private TableEntityVisitor() {
    this.params = new ArrayList<>();
  }

  public static TableEntityVisitor visit(Entity entity) {
    TableEntityVisitor visitor = new TableEntityVisitor();
    switch (entity.getModifiedFlag()) {
      case INSERT:
        visitor.visitInsert(entity);
        break;
      case UPDATE:
        visitor.visitUpdate(entity);
        break;
      default:
        throw new ExecutionException("Unsupported modified flag");
    }
    return visitor;
  }

  private void visitUpdate(Entity entity) {
    String tableName = entity.getTableName();
    String sets = entity.getColumns().stream().map(column -> {
      params.add(new Parameter(column.getValue()));
      return String.format("%s = ?", column.getColumnName());
    }).collect(joining(","));
    ValueColumn idColumn = entity.getId().orElseThrow(() -> new ExecutionException(
        String.format("Table %s can't update with id, because it don't have id column!",
            entity.getTableName())));
    params.add(new Parameter(idColumn.getValue()));
    this.sql = String.format("update %s set %s where %s = ?", tableName, sets,
        idColumn.getColumnName());
  }

  private void visitInsert(Entity entity) {
    String tableName = entity.getTableName();
    List<ValueColumn> valueColumns = new ArrayList<>(entity.getColumns());
    entity.getId().ifPresent(idColumn -> {
      if (!idColumn.isNullValue()) {
        valueColumns.add(idColumn);
      }
    });
    String columns = valueColumns.stream()
        .map(ValueColumn::getColumnName)
        .collect(joining(","));
    String values = valueColumns.stream()
        .map(column -> {
          params.add(new Parameter(column.getValue()));
          return "?";
        })
        .collect(joining(","));
    this.sql = String.format("insert into %s(%s) values(%s)", tableName, columns, values);
  }
}
